package view;

import java.util.ArrayList;
import java.util.Iterator;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;

import mathematics.Matrix4x4;
import mathematics.geometry.BezierCurve;
import mathematics.geometry.Geometry;
import mathematics.geometry.PolylineGeometry;
import matrix.GraphDoor;
import matrix.GraphNode;
import model.BaseModel;
import model.DoorModel;
import model.Orientation;
import model.RoomModel;

import settings.Settings;
import utils.Point3D;
import view.trajectory.TransitionTrajectoryView;
import graphics.engine.*;

public class DoorView extends BaseView
{
	public RoomView firstRoom;
	public RoomView secondRoom;
	
	public Geometry linkGeometry;
	
	public Point3D[] doorNorthPoints;
	public Point3D[] doorSouthPoints;
	public Point3D[] doorEastPoints;
	public Point3D[] doorWestPoints;
	
	private ArrayList<TransitionTrajectoryView> subtrajectories;
	
	private static float DoorWidth = 60;

	public int currentReverseIndex = 0;
	public int currentIndex = 0;
	
	public DoorView(BaseModel model, RoomView firstRoom, RoomView secondRoom)
	{
		super(model);
		this.firstRoom = firstRoom;
		this.secondRoom = secondRoom;
		this.linkGeometry = new PolylineGeometry();
		
		this.subtrajectories = new ArrayList<TransitionTrajectoryView>();
				
		doorNorthPoints = new Point3D[8];
		doorNorthPoints[0] = new Point3D(0- DoorWidth / 2.0f,0);
		doorNorthPoints[1] = new Point3D(0- DoorWidth / 2.0f,DoorWidth);
		doorNorthPoints[2] = new Point3D(DoorWidth * 0.259 - DoorWidth / 2.0f, DoorWidth *0.969);
		doorNorthPoints[3] = new Point3D(DoorWidth * 0.5- DoorWidth / 2.0f, DoorWidth *0.866);
		doorNorthPoints[4] = new Point3D(DoorWidth *0.707- DoorWidth / 2.0f, DoorWidth *0.707);
		doorNorthPoints[5] = new Point3D(DoorWidth *0.866- DoorWidth / 2.0f, DoorWidth *0.5);
		doorNorthPoints[6] = new Point3D(DoorWidth *0.966- DoorWidth / 2.0f, DoorWidth *0.259);
		doorNorthPoints[7] = new Point3D(DoorWidth- DoorWidth / 2.0f,0);

		doorSouthPoints = new Point3D[8];
		doorSouthPoints[0] = new Point3D(0 - DoorWidth / 2.0f,0);
		doorSouthPoints[1] = new Point3D(0 - DoorWidth / 2.0f,- DoorWidth);
		doorSouthPoints[2] = new Point3D(DoorWidth * 0.259 - DoorWidth / 2.0f , - DoorWidth *0.969);
		doorSouthPoints[3] = new Point3D(DoorWidth * 0.5 - DoorWidth / 2.0f, - DoorWidth *0.866);
		doorSouthPoints[4] = new Point3D(DoorWidth *0.707 - DoorWidth / 2.0f,-  DoorWidth *0.707);
		doorSouthPoints[5] = new Point3D(DoorWidth *0.866 - DoorWidth / 2.0f, - DoorWidth *0.5);
		doorSouthPoints[6] = new Point3D(DoorWidth *0.966 - DoorWidth / 2.0f, - DoorWidth *0.259);
		doorSouthPoints[7] = new Point3D(DoorWidth - DoorWidth / 2.0f,0);


		doorEastPoints = new Point3D[8];
		doorEastPoints[0] = new Point3D(0,  - DoorWidth / 2.0f);
		doorEastPoints[1] = new Point3D(0,DoorWidth - DoorWidth / 2.0f);
		doorEastPoints[2] = new Point3D(DoorWidth * 0.259 , DoorWidth *0.969 - DoorWidth / 2.0f);
		doorEastPoints[3] = new Point3D(DoorWidth * 0.5, DoorWidth *0.866 - DoorWidth / 2.0f);
		doorEastPoints[4] = new Point3D(DoorWidth *0.707, DoorWidth *0.707 - DoorWidth / 2.0f);
		doorEastPoints[5] = new Point3D(DoorWidth *0.866, DoorWidth *0.5 - DoorWidth / 2.0f);
		doorEastPoints[6] = new Point3D(DoorWidth *0.966, DoorWidth *0.259 - DoorWidth / 2.0f);
		doorEastPoints[7] = new Point3D(DoorWidth,0 - DoorWidth / 2.0f);

		doorWestPoints = new Point3D[8];
		doorWestPoints[0] = new Point3D(0,0- DoorWidth / 2.0f);
		doorWestPoints[1] = new Point3D(0,DoorWidth - DoorWidth / 2.0f);
		doorWestPoints[2] = new Point3D(- DoorWidth * 0.259 , DoorWidth *0.969 - DoorWidth / 2.0f);
		doorWestPoints[3] = new Point3D(- DoorWidth * 0.5, DoorWidth *0.866 - DoorWidth / 2.0f);
		doorWestPoints[4] = new Point3D(- DoorWidth *0.707, DoorWidth *0.707 - DoorWidth / 2.0f);
		doorWestPoints[5] = new Point3D(- DoorWidth *0.866, DoorWidth *0.5 - DoorWidth / 2.0f);
		doorWestPoints[6] = new Point3D(- DoorWidth *0.966, DoorWidth *0.259 - DoorWidth / 2.0f);
		doorWestPoints[7] = new Point3D(- DoorWidth,0 - DoorWidth / 2.0f);
	}
	
	public void Draw(GLAutoDrawable drawable, ProgramShader programShader) 
	{
		this.update();
		
		GL gl = drawable.getGL();
		Point3D endPoint;

		DoorModel model = (DoorModel)this.getModel();
		
		if(this.secondRoom.isNodeShape())
		{
			endPoint = Point3D.sum(((RoomModel)secondRoom.getModel()).getCenter(), this.secondRoom.getModel().getPosition());
		}
		else
		{
			endPoint = Point3D.sum(((RoomModel)secondRoom.getModel()).getPosition(), model.AnchoragePoint);
		}
		
		
		if(this.mustDrawLink())
		{
			this.DrawLink(drawable, programShader);
		}

		if ( this.mustDrawDoor() )
		{
			gl.glUniformMatrix4fv(programShader.WorldMatrixUniformID, 1, false, this.WorldMatrix(), 0);		

			gl.glLineWidth(1.0f);
			gl.glBegin(GL.GL_LINE_STRIP);
			gl.glColor3f(0, 0, 0);
				
			Point3D[] doorPoints = new Point3D[8];
			
			if( (model.Orientation == Orientation.North && this.firstRoom.isExpendedShape() ) || (model.Orientation == Orientation.South && this.secondRoom.isExpendedShape()))
			{
				doorPoints = doorNorthPoints;
			}
			else if( (model.Orientation == Orientation.South && this.firstRoom.isExpendedShape()) || (model.Orientation == Orientation.North && this.secondRoom.isExpendedShape()))
			{
				doorPoints = doorSouthPoints;
			}
			else if( (model.Orientation == Orientation.East&& this.firstRoom.isExpendedShape()) || (model.Orientation == Orientation.West && this.secondRoom.isExpendedShape()))
			{
				doorPoints = doorEastPoints;
			}
			else 	
			{
				doorPoints = doorWestPoints;
			}

			if(!this.secondRoom.isExpendedShape())
			{
				for(int i = 0 ; i < doorPoints.length; i++)
				{
					gl.glVertex3d(model.AnchoragePoint.x() + doorPoints[i].x(), model.AnchoragePoint.y() + doorPoints[i].y(), 0);
				}	
				gl.glVertex3d(model.AnchoragePoint.x() + doorPoints[0].x(), model.AnchoragePoint.y() + doorPoints[0].y(), 0);
			}
			else
			{
				for(int i = 0 ; i < doorPoints.length; i++)
				{
					gl.glVertex3d(endPoint.x() + doorPoints[i].x(), endPoint.y() + doorPoints[i].y(), 0);
				}	
				gl.glVertex3d(endPoint.x() + doorPoints[0].x(), endPoint.y() + doorPoints[0].y(), 0);
			}
			gl.glEnd();	
		}	
	}
	
	@Override
	public void update() {
		// TODO Auto-generated method stub
		if(mustDrawLink())
		{
			DoorModel model = ((DoorModel)this.getModel());
			this.linkGeometry.clear();
			if(this.isCrossingRoom() && Settings.isStraightDoorLinks())
			{	
				if(this.firstRoom.isExpendedShape())
				{
					if(((RoomModel)this.firstRoom.getModel()).getMatrix() != null)
					{				
						GraphDoor firstRoomGraphDoor = ((RoomModel)this.firstRoom.getModel()).getMatrix().getDoorNode(model);
						this.linkGeometry.add(firstRoomGraphDoor.getPosition() );
					}
				}
				else
				{
					this.linkGeometry.add(Point3D.sum(((RoomModel)firstRoom.getModel()).getCenter(), this.firstRoom.getModel().getPosition()));

				}
		
				if(this.secondRoom.isExpendedShape())
				{
					if(((RoomModel)this.secondRoom.getModel()).getMatrix() != null)
					{				
						GraphDoor secondRoomGraphDoor = ((RoomModel)this.secondRoom.getModel()).getMatrix().getDoorNode(model);
						this.linkGeometry.add(secondRoomGraphDoor.getPosition()); 
					}
				}
				else
				{
					this.linkGeometry.add(Point3D.sum(((RoomModel)secondRoom.getModel()).getCenter(), this.secondRoom.getModel().getPosition())); 
				}				
			}	
			else if(this.isCrossingRoom() && Settings.isBezierLinks())
			{
				Point3D p0;
				Point3D p1;
				Point3D p2;
				Point3D p3;
				
				GraphDoor firstRoomGraphDoor = ((RoomModel)this.firstRoom.getModel()).getMatrix().getDoorNode(model);
				GraphNode firstRoomNextGraphNode = (GraphNode) ((RoomModel)this.firstRoom.getModel()).getMatrix().getNeighbours(firstRoomGraphDoor).get(0);
				
				GraphDoor secondRoomGraphDoor = ((RoomModel)this.secondRoom.getModel()).getMatrix().getDoorNode(model);
				GraphNode secondRoomNextGraphNode = (GraphNode) ((RoomModel)this.secondRoom.getModel()).getMatrix().getNeighbours(secondRoomGraphDoor).get(0);
			
					
				if(this.firstRoom.isNodeShape())
				{
					p0 = ((RoomModel)this.firstRoom.getModel()).computedPosition();		
					p1 = firstRoomNextGraphNode.getPosition();
					
				}
				else
				{
					p0 = firstRoomGraphDoor.getDoorModel().AnchoragePoint;
					p1 = secondRoomNextGraphNode.getPosition();
				}
			
					
				if(this.secondRoom.isNodeShape())
				{
					p2 = secondRoomNextGraphNode.getPosition();
					p3 = ((RoomModel)this.secondRoom.getModel()).computedPosition();
				}
				else
				{
					p2 = firstRoomNextGraphNode.getPosition();
					p3 = secondRoomGraphDoor.getDoorModel().AnchoragePoint;
				}
				
				this.linkGeometry = new BezierCurve(p0,p1,p2,p3, (float)Settings.BezierWeightFactor, 20);
			}	
			else if(this.isCrossingRoom() && Settings.isPolylineLinks())
			{
				GraphDoor firstRoomGraphDoor = ((RoomModel)this.firstRoom.getModel()).getMatrix().getDoorNode(model);
				GraphNode firstRoomNextGraphNode = (GraphNode) ((RoomModel)this.firstRoom.getModel()).getMatrix().getNeighbours(firstRoomGraphDoor).get(0);	
				
				if(!this.firstRoom.isExpendedShape())
				{
					this.linkGeometry.add(((RoomModel)this.firstRoom.getModel()).computedPosition());
					this.linkGeometry.add(firstRoomNextGraphNode.getPosition());			
				}
				
				//gl.glVertex3f((float)this.model.AnchoragePoint.x(), (float)this.model.AnchoragePoint.y(),  0.0f);
			
				GraphDoor secondRoomGraphDoor = ((RoomModel)this.secondRoom.getModel()).getMatrix().getDoorNode(model);
				GraphNode secondRoomNextGraphNode = (GraphNode) ((RoomModel)this.secondRoom.getModel()).getMatrix().getNeighbours(secondRoomGraphDoor).get(0);

				if(!this.secondRoom.isExpendedShape())
				{
					this.linkGeometry.add(secondRoomNextGraphNode.getPosition());
					this.linkGeometry.add(((RoomModel)this.secondRoom.getModel()).computedPosition());	
				}

			}
		}
	}

	private boolean mustDrawDoor() 
	{
		return this.firstRoom.isExpendedShape() || this.secondRoom.isExpendedShape();
	}

	public boolean mustDrawLink()
	{
		return this.firstRoom.isNodeShape() || this.secondRoom.isNodeShape();
	}
	
	public void DrawLink(GLAutoDrawable drawable, ProgramShader programShader)
	{
		GL gl = drawable.getGL();
		gl.glUniformMatrix4fv(programShader.WorldMatrixUniformID, 1, false, this.WorldMatrix(), 0);		

		gl.glLineWidth(1.0f);			
		gl.glBegin(GL.GL_LINE_STRIP);
		
		if(Settings.HideUnnecessaryObjects && this.subtrajectories.size() == 0)
		{
			gl.glColor4f(0.0f, 0.0f, 0.0f, Settings.AlphaUsedForHidding);
		}
		else
		{
			gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
		}
		
		for(Iterator<Point3D> it = this.linkGeometry.iterator(); it.hasNext();)
		{
			Point3D point = it.next();
			gl.glVertex3d(point.x(), point.y(), point.z());
		}
		gl.glEnd();
	}
	
	public boolean isCrossingRoom()
	{
		return true;
	}
	
	public float[] WorldMatrix()
	{
		Matrix4x4 worldMatrix = new Matrix4x4();
		
		worldMatrix.setToIdentity();
		
		return worldMatrix.toFloatArray();
	}
	
	public void resetStat()
	{
		this.currentIndex = 0;
		this.currentReverseIndex = 0;
	}
	
	public void addSubtrajectoryView(TransitionTrajectoryView trajView) {
		this.getSubtrajectories().add(trajView);
	}

	public ArrayList<TransitionTrajectoryView> getSubtrajectories() {
		return subtrajectories;
	}

	public void setSubtrajectories(ArrayList<TransitionTrajectoryView> subtrajectories) {
		this.subtrajectories = subtrajectories;
	}
}
